perm filename TCPRA.MAC[IP,SYS] blob sn#680229 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-TCP>TCPRA.MAC.40303  6-May-82 16:49:08, Edit by CLYNN
; Fix transmitter exceeding window leading to negative window bug
; Pass time to EMTPKT, work on UPDWND/NUWNDO
;<403-TCP>TCPRA.MAC.40301 29-Jan-82 15:07:48, Edit by CLYNN
; Updated for TCP release 3
;[BBND]<401-TCP>TCPRA.MAC.96, 11-Aug-81 13:23:00, Ed: CLYNN
; Fix: internet fork loop due to full tvt input buffer at REAS20
;[BBNF]<401-TCP>TCPRA.MAC.95, 10-Jul-81 15:33:00, Ed: CLYNN
; Fix: TVTL zero because closing problems in REASEM to 16 & NUWNDO
; FIN with URG in REAS16, No room in RAINI

	SEARCH	INPAR,TCPPAR,PROLOG
	TTITLE	TCPRA
	SUBTTL	TCP Reasembler, William W. Plummer, 19JAN77
	SWAPCD


COMMENT	!

	The REASEMBLER is called with TCB set up to point at a
	(locked) connection block.  Its function is to transfer
	data from packets queued for it by the Inputprocessor
	into user buffers queued by RECV calls on the TCP.
	The REASEMBLER also processes certain control bits
	in the packets such FIN.  Once handled,
	the PACKETIZER is signaled so that it may generate an
	ACK for the packet.  TCP Virtual Terminal
	characters are moved into line buffers via the
	TELNET protocol routines in the NVT code.



* REASEM ...  3 ...... Fill user RECV buffers from packets received

  PRCEOL ... 10 ...... Process EOL
  PRCFIN ... 10 ...... Process FIN
  PRCDAT ... 11 ...... Process data

* NUWNDO ... 12 ...... Update receive window after packet processed or RECV

* FLSRBF ... 14 ...... Flush receive buffers
* RAINI .... 15 ...... Initialize RA process block
	!

; Reasembler

;TCB/	(Extended) Pointer to connection block
;
;	CALL REASEM
;Ret+1:	always

REASEM::LOCAL <BYTNUM,XFRCNT,RCVLFT,LINBLK>
	PUSH P,PKT
	PUSH P,TPKT
	PUSH P,BFR
	SETO LINBLK,		; Indicate no terminal line locked
				; <0 must check TTVT, not locked
				;    (NOTSYN or no TVT assigned)
				; =0 non-standard, locked
				; >0 have TTVT (& TVTL & R-SYNCED) & locked

	JE TTVT,(TCB),REASMN	; Jump if not a TCP Virtual Terminal
	LOAD T1,TRSYN,(TCB)	; State of receive side
	CAIE T1,SYNCED		; OK to pass data now?
	  JRST REASMN		; No.  But process control
	LOAD T2,TVTL,(TCB)	; Get the line number
	JUMPE T2,REASMN		; Jump if none assigned yet or gone away
	CALL TVTCHK		; Lock the terminal data base
	  JRST [JUMPLE T2,REASMN ;Nothing locked (inactive, becoming active)
		SETZ T2,	; Locked & non-standared so
		JRST .+1]	; must update LINBLK
	MOVEM T2,LINBLK		; Save here for later
REASMN:


; Top of main loop:

; Check the queue of packets from the InputProcessor.  If there are
;  no packets, there is nothing that the Reassembler can do.

REASM0:	LOAD PKT,QNEXT,<+TCBRPQ(TCB)> ; Get pointer to first thing on Q
	CAIN PKT,TCBRPQ(TCB)	; Receive packet queue empty?
	  JRST REASMX		; Yes.  Get out.
	SETSEC PKT,INTSEC	; Make extended address
	LOAD T1,PIDO,(PKT)	; Internet data offset in words
	XMOVEI TPKT,PKTELI(PKT)	; Pointer to Internet portion of packet
	ADD TPKT,T1		; Pointer to TCP portion of packet

; Set BFR to 0 if this is a TVT so as to avoid code which
; fiddles with normal buffers.

	JE TTVT,(TCB),REAS0A	; If not TVT go get BFR
	MOVX BFR,0		; Indicate no normal buffer
	SKIPG T2,LINBLK		; Set arg.
	  JRST REAS0A		; Not a TVT or gone away
	CALL TVTISP		; Get space in input buffer
	JFCL ;JUMPN T1,REASM3		; Continue if there is some
	JFCL ;JN TRPP,(TCB),REASMX	; Get out if part. pkt and no space
	JRST REASM3		; Forge ahead to (say) open conn/closing
REAS0A:

; Try to find a user buffer for filling.  This could be the Receive
;  current buffer left from a previous pass or one queued from
;  a user RECV call.

	LOAD BFR,TRCB,(TCB)	; Get 0 or receive current buffer
	SETSEC BFR,INTSEC	; Make extended address
	TRNE BFR,-1		; Is there a current buffer?
	  JRST REASM3		; Go use what is left of it
	LOAD BFR,QNEXT,<+TCBRBQ(TCB)>	; Pointer to first buffer queued
	CAIE BFR,TCBRBQ(TCB)	; Empty if that is the queue head
	  JRST REASM1		; Go dequeue the buffer and use it

; No buffer available. If there is a partially processed packet,
;  we can do no more.  Otherwise there may be controls (SYN)
;  which can be handled.  This allows a SYN to be ACKd and thus a
;  connection to open before any user RECVs have been done.

	JN TRPP,(TCB),REASMX	; Get out if there is a partial packet
	MOVX BFR,0		; Indicate no buffer to use.
	JRST REASM3		; Proceed

; Dequeue buffer at the head of the receive buffer queue

REASM1:	SETSEC BFR,INTSEC	; Make extended address
	MOVE T1,BFR		; Pointer to the buffer
	CALL DQ			; Dequeue it
	STOR BFR,TRCB,(TCB)	; And remember as the current buffer

REASM3:	LOAD RCVLFT,TRLFT,(TCB)
	JE TRPP,(TCB),REASM4	; Jump if not continuing a packet
	LOAD BYTNUM,TRCBY,(TCB)	; Where to resume in this packet
	JRST REAS13		; Go process the remainder


; First time we have seen this packet.  Flush it unless there is
;  some unseen stuff in it.

REASM4:	AOS RAPKCT		; Count packets seen by Reassembler
	MOVX T1,PT%TRA
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKT		; Yes
	MOVE T1,RCVLFT		; Recv Left -- start of "The present"
	LOAD T2,TRWND,(TCB)	; Get current window width
	ADD T2,T1		; Form Recv.Right -- beginning of "Past"
	ADDI T2,1		; Allow SYN thru 0-window crock
	MODSEQ T2		; T1,T2 are Left and Right of the past
	LOAD T3,PSEQ,(TPKT)	; Sequence of the packet
	LOAD T4,PESEQ,(PKT)	; Get end + 1 from packet
	CALL OVRLAP		; Packet included in the past?
	JUMPN T1,REASM5		; Jump if not.

	MOVEI T1,RADLAY		; Select Reassembler delay histogram
	SKIPE STATF		; Actually taking statistics?
	  CALL TSTAMP		; Yes.  Process the timestamp.
	MOVX T1,PT%XX4		; Code for reassembler
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKT		; Yes, Call the packet printer
	MOVE T1,PKT		; Pointer to this useless packet
	CALL DQ			; Dequeue it 
	CALL RETPKT		; Give space to freestorage
	JRST REASM0		; Try the next packet.
REASM5:

; If Left is within the current packet, there is something which
; can be reassembled out of it.

	LOAD T1,PSEQ,(TPKT)	; Start of the packet
	MOVE T2,RCVLFT		; Next thing needed for reassembly
	LOAD T3,PESEQ,(PKT)	; End of the packet
	CALL CHKWND		; Left within the packet?
	JUMPE T1,REASMX		; Jump if not.  Must wait for it to show

; Setup BYTNUM to be the byte number within the packet where data
; handling should start.

	LOAD RCVLFT,TRLFT,(TCB)	; Get updated copy
;REAS10:
	MOVE BYTNUM,RCVLFT	; Next to be reassembled
	LOAD T1,PSEQ,(TPKT)	; Start of packet
	SUB BYTNUM,T1		; Offset into data
	JUMPLE BYTNUM,REAS12	; No control to worry about
	LOAD T1,PSYN,(TPKT)	; Get value of SYN bit
	SUBI BYTNUM,0(1)	; Discount space taken by SYN
REAS12:

; Setup XFRCNT to be the number of bytes to transfer out of the
; packet into the user buffer.

REAS13:	LOAD XFRCNT,PIPL,(PKT)	; Get total length
	LOAD T1,PIDO,(PKT)	; Number of words in Internet header
	LOAD T2,PTDO,(TPKT)	; Number of words in TCP header
	ADD T1,T2		; Number of header words
	ASH T1,2		; Number of header bytes
	SUB XFRCNT,T1		; Number of TCP data bytes
	SUB XFRCNT,BYTNUM	; Forget already processed bytes
	PUSH P,XFRCNT		; Save packet count (number available)
	SKIPG T2,LINBLK		; Is this a TVT w/ standard data block?
	 TDZA T1,T1		; No. Assume no buf and no space
	  CALL TVTISP		; Yes.  Get amount of space to T1
	JUMPE BFR,REAS14	; Jump if no buffer
	LOAD T1,BCNT,(BFR)	; Get number of holes in the buffer
REAS14:
	CAMLE XFRCNT,T1		; Min of available bytes and space
	  MOVE XFRCNT,T1	; is the actual transfer count
	JUMPLE XFRCNT,REAS15	; Jump if nothing to transfer.
	ADDM XFRCNT,BYTRCT	; Count bytes received
	MOVE T1,BYTNUM		; Where to start transfer from packet
	LOAD T3,PTDO,(TPKT)	; Get TCP data offset in words
	IDIVI T1,4		; Get words and byte into data
	ADD T1,T3		; Get word offset from TPKT
	HLL T1,[POINT 8,.-.(TPKT),-1
		POINT 8,.-.(TPKT),07
		POINT 8,.-.(TPKT),15
		POINT 8,.-.(TPKT),23](T2)
	SKIPG T2,LINBLK		; Addr of dynamic data area
	  JRST REA14A		; None
	MOVE T3,XFRCNT		; How much to transfer
	CALL PRCTVT		; Process TVT chr on line in T2
	JRST REAS15
REA14A:
	MOVE T2,XFRCNT		; How much to transfer
	CALL PRCDAT		; Process the data
REAS15:	POP P,T1		; Restore the packet count

; If the packet has been emptied into a buffer after the connection
; has become synchronized in the receive direction, process the
; trailing controls and flush the packet.  If the buffer was
; filled, report the fact to the user.

	LOAD T2,TRSYN,(TCB)	; Get receive state
	CAIE T2,SYNCED		; Synchronized?
	 CAIN T2,FINRCV		; or FIN Received?
	  CAIA			; Yes.
	   JRST REAS19		; No. Save as partial packet.
; ?? See if user buffer is full here for USRBFF a la reas18
	CAME T1,XFRCNT		; Emptied all data from the packet?
	  JRST REAS18		; No.
	JN TTVT,(TCB),REAS16	; Assume EOL and buffer if TVT
	JUMPN BFR,REAS16	; Into a buffer?
	JN PEOL,(TPKT),REAS18	; Lack buffer to report EOL in
REAS16:

; Packet empty, finish it off & loop back for next

	SETZRO TRPP,(TCB)	; Indicate no partial packet waiting

; See if we can leave receive urgent mode.  The urgent pointer must
; coincide with the end of a packet plus one.  So, we need only test the
; PESEQ for equality with the urgent pointer to tell if data up to the
; urgent pointer has been given to the user.

	JE TRURG,(TCB),REAS17	; Forget if not in receive urgent mode
	LOAD T1,PESEQ,(TPKT)	; Get the end plus one of this packet
	LOAD T2,TRURP,(TCB)	; And the receive urgent pointer
	CAMGE T1,T2		; Will the urgent pointer be acked? (FIN)
	  JRST REAS17		; No.
	SETZRO TRURG,(TCB)	; Leave receive urgent mode
REAS17:
	JN TTVT,(TCB),REA17A	; No EOL processing on TVTs
	JE PEOL,(TPKT),REA17A
	CALL PRCEOL		; Process EOL
REA17A:

	JE PFIN,(TPKT),REA17B
	CALL PRCFIN		; Process FIN if present, generate ack
REA17B:
	LOAD T1,PESEQ,(PKT)	; Get the sequence number following Pkt
	STOR T1,TRLFT,(TCB)	; Set the new Left

	CALL NUWNDO		; Update the window, maybe generate ACK

	MOVEI T1,RADLAY		; Select Reassembler delay
	SKIPE STATF		; Taking statistics right now?
	  CALL TSTAMP		; Yes, process the timestamp
	MOVX T1,PT%XX4		; "Reassembled" code
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKT		; Yes, Print the packet
	MOVX T1,PT%TDR
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKT		; Yes

; Since we have completely finished with this packet, dequeue it
; and return the space to free storage.

	MOVE T1,PKT		; Pointer to the packet
	CALL DQ			; Dequeue it
	CALL RETPKT		; Free the area
	JRST REASM0		; And process the next packet.

; Here when more remains in packet

REAS18:	JUMPE BFR,REAS19	; Jump if no buffer
	JN BCNT,(BFR),REAS19	; Jump if buffer not filled
	MOVX T1,OK		; Indicate buffer is good
	CALL USRBFF		; User buffer filled routine
	SETZRO TRCB,(TCB)	; Indicate no current buffer anymore
REAS19:

; Save the partial packet for the next time through.

	SETONE TRPP,(TCB)	; Set the partial packet waiting bit
	MOVE T1,XFRCNT		; Number transferred
	ADD T1,BYTNUM		; Where the transfer started
	STOR T1,TRCBY,(TCB)	; Is where to resume in the packet

	JUMPN BYTNUM,REAS20	; First time we have
	JE PSYN,(TPKT),REAS20	; Seen a packet with a SYN in it?
	ADD RCVLFT,XFRCNT	; Yes. Update Left
	STOR RCVLFT,TRLFT,(TCB)

	MOVX T1,↑D500
	CALL ENCPKT ;FRCPKT	; Get it ACK'd
REAS20:

	JUMPE BFR,REASMX	; If TVT input full, stop now
	JE TRCB,(TCB),REASM0	; Try to get another buffer from queue

REASMX:	SKIPL T2,LINBLK		; Do we have a term line locked?
	  CALL ULKTTY		; Yes.  Unlock it
	POP P,BFR
	POP P,TPKT
	POP P,PKT
	RESTORE
	RET

; Process EOL

;TCB/	(Extended) Locked connection block
;BFR/	(Extended) Current buffer
;
;	CALL PRCEOL
;Ret+1:	always

PRCEOL:	MOVX T1,<<OK>B7+TCP%EL>	; OK code, with end of letter flag
	CALL USRBFF		; Tell user buffer filled
	MOVX BFR,0		; Indicate no current buffer
	STOR BFR,TRCB,(TCB)
	RET



; Process FIN

;TCB/	(Extended) Locked connection block
;
;	CALL PRCFIN
;Ret+1:	always

PRCFIN:	MOVX T1,FINRCV		; FIN Received state
	STOR T1,TRSYN,(TCB)	; Set into TCB
	AOS FINRCT		; Count FINs received
	MOVX T1,↑D100
	CALL ENCPKT		; Make sure its ACKed promptly
	RET

; Process data from packet

;TCB/	(Extended) Locked connection block
;PKT/	(Extended) Packet
;TPKT/	(Extended) pointer to TCP part of packet
;BFR/	(Extended) Buffer
;T1/	Byte pointer into packet
;T2/	Count of bytes to transfer to buffer
;
;	CALL PRCDAT
;Ret+1:	always

PRCDAT:	LOCAL <PKTPTR,XFRCNT>
	DMOVEM T1,PKTPTR
;PRCDA1:
	CALL SETTUM		; Set TCP's usermode map

	MOVE T1,PKTPTR		; Source byte pointer
	LOAD T2,BPTR,(BFR)	; Destination byte pointer
	MOVE T3,XFRCNT		; Number to do
	SETZ T4,		; Monitor-to-user transfer
	CALL XFRDAT		; Do the data transfer
	STOR T2,BPTR,(BFR)	; Store back updated pointers
	MOVEM T1,PKTPTR
;PRCDA4:
	LOAD T1,BCNT,(BFR)	; Get number of holes in buffer at start
	SUB T1,XFRCNT		; Reduce by number transferred
	STOR T1,BCNT,(BFR)	; Update the count in the buffer
	LOAD T3,TRBS,(TCB)	; Get receive bufferspace (due to RECVs)
	SUB T3,XFRCNT		; Remove space just filled from window
	STOR T3,TRBS,(TCB)
;;;;	CALL USRBUD		; Update the user's buffer header
	CALL USTTUM
PRCDAX:	RESTORE
	RET

; Update Receive Window

; Whenever a user RECV increases the available buffer space or
; after processing a packet entirely the size of the window being
; sent to the remote TCP is determined and set into the TCB.  If
; processing the packet has moved Received Left, the Packetizer is
; signaled so it will generate an ACK.

;TCB/	(Extended) Locked Connection block
;	CALL NUWNDO
;Ret+1:	always

NUWNDO::LOAD T1,TRBS,(TCB)	; Currently available buffer space

	JE TTVT,(TCB),NUWND1	; If a TVT
	SETZ T1,		; Assume no space
	LOAD T2,TVTL,(TCB)	; Unless a line
	JUMPE T2,NUWND1		; Has been assigned
	CALL STADYN		; Get line's data block
	  JRST NUWND1		; ??  address
	CALL TVTISP		; Get TTY input space
NUWND1:

; Now have available input space, maybe zero

	LOAD T2,TRLWN,(TCB)	; Seq # of last receive-right reported
	JUMPL T2,NUWND5		; Not yet available, use actual space
	LOAD T3,TRLFT,(TCB)	; Compute unused space from last window
	SUB T2,T3
	MODSEQ T2
	CAIL T2,<.RTJST(-1,PIPL)> ;CAML T2,[MAXSEQ/2] ; Beware negative
	  SETZ T2,		; Window (transmitter sent too much)
	JUMPLE T1,NUWND4	; No space, don't increase offered window
				; (but don't shrink it either)
	MOVE T4,INTXPB		; Estimated packet size
	SUBI T4,MINIHS+MINTHS	; w/o minimal headers
	JFCL ; LSH T4,1		; How optimistic are we??
	CAIG T1,(T4)		; If user space is less
	  MOVE T1,T4		; Be optimistic
	CAILE T1,<.RTJST(-1,PIPL)> ; But not more so than
	  MOVE T1,<.RTJST(-1,PIPL)> ; Size of window field

	MOVE T3,T2		; Remaining space, last window
	LSH T3,1		; Factor is 1/2
	CAMLE T3,T1		; If remaining .gt. 1/2 actual
NUWND4:	  MOVE T1,T2		; Don't report it (no silly windows)
NUWND5:	STOR T1,TRWND,(TCB)	; Offered window space

; Check if sender should be notified of ACKed data or new window info

	LOAD T3,TRSYN,(TCB)	; Check connection state
	LOAD T4,TSSYN,(TCB)
	CAIE T4,SYNABL		; If send side doesn't have a seq #
	 CAIN T3,SYNABL		; or don't know foreign address
	  RET			; Cannot send a packet

	LOAD T4,TRLFT,(TCB)	; Current ACK point
	LOAD T3,TRLAK,(TCB)	; Last reported ACK point
	SUB T2,T1		; Non-zero if new window info

;	LOAD T1,TSMRT,(TCB) ; (or TMNRT) ; Estimated time
;	LSH T1,1		; Pause factor
	MOVX T1,↑D250		; Estimated time too long, get RXs

	CAMN T3,T4		; If new ACK point or
	 SKIPE T2		; New window space
	  CALL ENCPKT		; Get a packet sent in a bit
	RET

; Flush Receive Buffers		Called when aborting a connection

;TCB/	(Extended) Locked connection block
;T1/	Code (377B7) to be left in the buffer header for user to see
;		XLP+12.	EFP+7.	ELP+7.	ELP+14.	E?T+? (TVTs)
;
;	CALL FLSRBF
;Ret+1:	always

FLSRBF::LOCAL <CODE>
	PUSH P,BFR
	MOVEM T1,CODE
	LOAD BFR,TRCB,(TCB)	; Get the current receive buffer if any
	SETZRO TRCB,(TCB)	; Forget there is one
	JUMPE BFR,FLSRB2	; Jump if no current buffer
	SETSEC BFR,INTSEC	; Make extended address

FLSRB1:	MOVE T1,CODE
	LSH T1,↑D<36-8>		; Put into postion
	CALL USRBFF		; Indicate user buffer "filled"
FLSRB2:	LOAD BFR,QNEXT,<+TCBRBQ(TCB)>	; First thing on queue
	CAIN BFR,TCBRBQ(TCB)	; If that is the head, queue now empty
	  JRST FLSRBX
	SETSEC BFR,INTSEC	; Make extended address
	MOVE T1,BFR
	CALL DQ			; Dequeue the buffer
	JRST FLSRB1

FLSRBX:	POP P,BFR
	RESTORE
	RET

; RAINI			Initialize RA process block

;	CALL RAINI
;Ret+1:	Always, T1 zero if error

RAINI::	LOCAL <PRC>

	MOVEI PRC,RA		; Pointer to the Process block for RA
	MOVX T1,QSZ		; Size of a queue head
	CALL GETBLK		; Head must be in same section as items
	JUMPE T1,RAINIX		; No room
	MOVEM T1,PRCQ(PRC)	; Input queue
	CALL INITQ		; Initialize it

	XMOVEI T1,PRCLCK(PRC)	; Lock
	CALL CLRLCK		; Initilize it

	XMOVEI T1,REASEM	; The Reassembler function
	MOVEM T1,PRCROU(PRC)	; Routine address
	SETOM PRCWAK(PRC)	; No run time yet
	MOVE T1,[<GIW TCBQRA,TCB>]; Offset of RA queue in TCB
	MOVEM T1,PRCQOF(PRC)	; Store process block
	MOVE T1,[<GIW TCBTRA,TCB>]; Offset of RA run time in TCB
	MOVEM T1,PRCWOF(PRC)	; Store in process block
	HRLOI T1,377777		; Infinity
	MOVEM T1,PRCSGT(PRC)	; Set time of most recent signal
	MOVEI T1,RARNCT		; Pointer to run counter
	MOVEM T1,PRCRNC(PRC)	; Put in standard place
	MOVEI T1,RAUSE		; Pointer to CPU use meter
	MOVEM T1,PRCTMR(PRC)	; Put in standard place
;	SETO T1,

RAINIX:	RESTORE
	RET

	TNXEND
	END